package util

import (
	"github.com/nuttech/bell"
	"gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
	"gitverse.ru/synapse/kubelatte/pkg/observability/logs"
	"os"
	"reflect"
	"strconv"
)

type LogStatus string

var (
	KbltMutator     = os.Getenv("KBLT_MUTATOR")
	KbltValidator   = os.Getenv("KBLT_VALIDATOR")
	KbltCreator     = os.Getenv("KBLT_CREATOR")
	KbltMainPort    = os.Getenv("KBLT_PORT_MAIN")
	IKbltMainPort   = 0
	KbltLoggerPort  = os.Getenv("KBLT_PORT_LOG")
	IKbltLoggerPort = 0
)

type ConditionStatus string

var StatusInfo []Status

type Status struct {
	TypeConfig string          `yaml:"typeConfig"`
	NameConfig string          `yaml:"nameConfig"`
	Status     ConditionStatus `yaml:"status"`
	Message    string          `yaml:"message"`
}

type RenderItem struct {
	Template             v1alpha1.Template
	Render               string
	Action               string
	CreationIsUniqueName bool
}

func IsMutatorRole() bool {
	return KbltMutator == "true"
}

func IsValidatorRole() bool {
	return KbltValidator == "true"
}

func IsCreatorRole() bool {
	return KbltCreator == "true"
}

func IsFullRole() bool {
	if IsMutatorRole() && IsValidatorRole() {
		return true
	}
	return false
}

func GlobalInit() {
	KbltMutator = os.Getenv("KBLT_MUTATOR")
	KbltValidator = os.Getenv("KBLT_VALIDATOR")
	KbltCreator = os.Getenv("KBLT_CREATOR")
	KbltMainPort = os.Getenv("KBLT_PORT_MAIN")
	KbltLoggerPort = os.Getenv("KBLT_PORT_LOG")

	if KbltMainPort == "" {
		IKbltMainPort = 9443
	} else {
		IKbltMainPort, _ = strconv.Atoi(KbltMainPort)
	}

	if KbltLoggerPort == "" {
		IKbltLoggerPort = IKbltMainPort + 1
	} else {
		IKbltLoggerPort, _ = strconv.Atoi(KbltLoggerPort)
	}
}

func MergeSliceWithoutDuplicate(aT []interface{}, aS []interface{}) []interface{} {
	for _, item := range aS {
		found := false
		if reflect.TypeOf(item).Kind() == reflect.Map {
			name, ok := item.(map[string]interface{})["name"]
			if ok {
				for i, origItem := range aT {
					if reflect.TypeOf(origItem).Kind() == reflect.Map {
						origName, ok := origItem.(map[string]interface{})["name"]
						if ok {
							if name == origName {
								found = true
								aT[i] = MergeStruct(origItem.(map[string]interface{}), item.(map[string]interface{}), true)
							}
						}
					}
				}
				if !found {
					aT = append(aT, item)
				}
			} else {
				aT = append(aT, item)
			}
		} else {
			if !contains(aT, item) {
				aT = append(aT, item)
			}
		}
	}

	return aT
}

func contains(s []interface{}, e interface{}) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

func MergeSliceCommon(aT []interface{}, aS []interface{}, dupl bool) []interface{} {
	if dupl {
		return MergeSliceWithoutDuplicate(aT, aS)
	} else {
		MergeSlice(aT, aS)
	}
	return nil
}

func MergeSlice(aT []interface{}, aS []interface{}) []interface{} {
	for _, item := range aS {
		found := false
		if item == nil {
			aT = append(aT, nil)
		}
		if reflect.TypeOf(item).Kind() == reflect.Map {
			name, ok := item.(map[string]interface{})["name"]
			if ok {
				for i, origItem := range aT {
					if reflect.TypeOf(origItem).Kind() == reflect.Map {
						origName, ok := origItem.(map[string]interface{})["name"]
						if ok {
							if name == origName {
								found = true
								aT[i] = MergeStruct(origItem.(map[string]interface{}), item.(map[string]interface{}), false)
							}
						}
					}
				}
				if !found {
					aT = append(aT, item)
				}
			} else {
				aT = append(aT, item)
			}
		} else {
			aT = append(aT, item)
		}
	}

	return aT
}

func MergeStruct(aT map[string]interface{}, aS map[string]interface{}, dupl bool) map[string]interface{} {
	for k, item := range aS {
		if item == nil {
			aT[k] = nil
			continue
		}
		if reflect.TypeOf(item).Kind() == reflect.Map {
			val, ok := aT[k]
			if ok {
				aT[k] = MergeStruct(val.(map[string]interface{}), item.(map[string]interface{}), dupl)
			} else {
				aT[k] = item
			}
		} else if reflect.TypeOf(item).Kind() == reflect.Slice {
			_, ok := aT[k]
			if ok {
				aT[k] = MergeSliceCommon(aT[k].([]interface{}), item.([]interface{}), dupl)
			} else {
				aT[k] = item
			}
		} else {
			aT[k] = item
		}
	}
	return aT
}

func SendEvent(eventName string, value interface{}) {
	err := bell.Ring(eventName, value)
	if err != nil {
		logs.Errorf("Can't send event %s. Values:%s, Error: %s", eventName, value, err)
	}
}

func SendEventWithErr(eventName string, value interface{}) error {
	err := bell.Ring(eventName, value)
	if err != nil {
		logs.Errorf("Can't send event %s. Error: %s", eventName, err)
	}
	return err
}

type Patch struct {
	Op       string      `json:"op"`
	From     string      `json:"from,omitempty"`
	Path     string      `json:"path"`
	OldValue interface{} `json:"-"`
	Value    interface{} `json:"value,omitempty"`
}
